﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Windows;
using System.Windows.Documents;
using System.Windows.Media;
using Expression=System.Linq.Expressions.Expression;

namespace RG.Common.WPF.Controls.SortableGridView
{
    public class ReflectionLambdaSort : IComparer
    {
        private static Func<object, IComparable> CreateGetter(Type objType, string propertyName)
        {
            var param = Expression.Parameter(typeof (object), "e");

            Expression body = Expression.Convert(
                Expression.PropertyOrField(Expression.TypeAs(param, objType), propertyName), typeof (IComparable));

            var getterExpression = Expression.Lambda<Func<object, IComparable>>(body, param);
            return getterExpression.Compile();
        }

        public ListSortDirection Direction { get; private set; }
        public Func<object, IComparable> Getter { get; private set; }

        public ReflectionLambdaSort(ListSortDirection direction, string column, Type t)
        {

            Direction = direction;
            Getter = CreateGetter(t, column);
        }

        public int Compare(object x, object y)
        {
            var v1 = Getter(x);
            var v2 = Getter(y);
            
            int cmp;

            if (ReferenceEquals(v1, null) && ReferenceEquals(v2, null)) cmp = 0;
            else if (ReferenceEquals(v1, null)) cmp = 1;
            else if (ReferenceEquals(v2, null)) cmp = -1;
            else cmp = v1.CompareTo(v2);

            return Direction == ListSortDirection.Ascending ? cmp : -cmp;
        }
    }

    public class ReflectionMulticolumnLambdaSort : IComparer
    {
        private static Func<object, IComparable> CreateGetter(Type objType, string propertyName)
        {
            var param = Expression.Parameter(typeof(object), "e");
            Expression body = Expression.Convert(Expression.PropertyOrField(Expression.TypeAs(param, objType), propertyName), typeof(IComparable));
            var getterExpression = Expression.Lambda<Func<object, IComparable>>(body, param);
            return getterExpression.Compile();
        }

        private readonly Dictionary<string, SortingColumn> Columns;
        public List<string> Column { get; set; }
        public ListSortDirection Direction { get; private set; }
        public Func<object, IComparable> Getter { get; private set; }

        public List<Func<object, IComparable>> Getters { get; set; }

        public ReflectionMulticolumnLambdaSort()
        {
            Columns = new Dictionary<string, SortingColumn>();
        }

        public void AddSortingSortDescription(ListSortDirection direction, string column, Type t)
        {
            Getter = CreateGetter(t, column);
            Columns.Add(column, new SortingColumn { Direction = direction, Getter = Getter });
        }

        public ReflectionMulticolumnLambdaSort(ListSortDirection direction, string column, Type t)
        {
            Direction = direction;
            Getter = CreateGetter(t, column);
        }

        public int Compare(object x, object y)
        {
            if (Columns != null)
            {
                var ff = new SortingColumn[Columns.Values.Count];
                Columns.Values.CopyTo(ff, 0);
                for (int i = 0; i < ff.Length; i++)
                {
                    var v1 = ((SortingColumn)ff.GetValue(i)).Getter(x);
                    var v2 = ((SortingColumn)ff.GetValue(i)).Getter(y);

                    int cmp;
                    if (v1 == null && v2 == null) cmp = 0;
                    else if (v1 == null) cmp = -1;
                    else if (v2 == null) cmp = 1;
                    else cmp = v1.CompareTo(v2);

                    if (cmp != 0)
                        return ((SortingColumn)ff.GetValue(i)).Direction == ListSortDirection.Ascending ? cmp : -cmp;
                }
            }
            return 0;
        }
    }

    public class SortAdorner : Adorner
    {
        private readonly static Geometry AscGeometry =
            Geometry.Parse("M 0,0 L 10,0 L 5,5 Z");

        private readonly static Geometry DescGeometry =
            Geometry.Parse("M 0,5 L 10,5 L 5,0 Z");

        public ListSortDirection Direction { get; private set; }

        public SortAdorner(UIElement element, ListSortDirection dir)
            : base(element)
        { Direction = dir; }

        protected override void OnRender(DrawingContext drawingContext)
        {
            base.OnRender(drawingContext);

            if (AdornedElement.RenderSize.Width < 20)
                return;

            drawingContext.PushTransform(
                new TranslateTransform(
                    AdornedElement.RenderSize.Width - 22,
                    (AdornedElement.RenderSize.Height - 5) / 2));

            drawingContext.DrawGeometry(Brushes.Black, null,
                                        Direction == ListSortDirection.Ascending ?
                                                                                     AscGeometry : DescGeometry);

            drawingContext.Pop();
        }
    }

    public class EnumAdorner : Adorner
    {
        
        public int Enum { get; private set; }

        public EnumAdorner(UIElement element, int num)
            : base(element)
        { Enum = num; }

        protected override void OnRender(DrawingContext drawingContext)
        {
            base.OnRender(drawingContext);

            if (AdornedElement.RenderSize.Width < 10)
                return;

            var x = (AdornedElement.RenderSize.Width - 10);
            var y = (AdornedElement.RenderSize.Height - 6)/2;

            drawingContext.DrawText
                (
                new FormattedText(Enum.ToString(),
                                  CultureInfo.GetCultureInfo("ru-Ru"),
                                  FlowDirection.LeftToRight,
                                  new Typeface("Verdana"), 7, Brushes.Black),
                new Point(x, y));

        }
    }

    public class SortingColumn
    {
        public Func<object, IComparable> Getter { get; set; }
        public ListSortDirection Direction { get; set; }
    }
}